
#include	<vector>
#include	<string>
#include	<stdio.h>
#include	<stdarg.h>
#include	<ctype.h>

#include	"qdxc/protocol.hpp"
#include	"qdxc/packet.h"

#include	"zutil/string.hpp"

using namespace std;

namespace	util = zutil;

namespace	QDX
{
    namespace	Client
    {

        Protocol::Protocol()
            :	m_daemonSocket(), m_pDaemonAddress(NULL), m_iMessageSerial(0)
        {
            m_bReady = false;

            char*	pszEnv = NULL;
            m_fileDebug = NULL;
            pszEnv = getenv ( "QDX_CLIENT_DEBUG" );
            if ( NULL != pszEnv ) {
                if ( strlen(pszEnv) > 4 ) {
                    m_fileDebug = fopen ( pszEnv, "w" );
                    if ( NULL == m_fileDebug ) {
                        m_fileDebug = stdout;
                    }
                } 
                else {
                    m_fileDebug = stdout;
                }

                setbuf ( m_fileDebug, NULL );
            }

            m_fileTrace = NULL;
            pszEnv = getenv ( "QDX_CLIENT_TRACE" );
            if ( NULL != pszEnv ) {
                if ( strlen(pszEnv) > 4 ) {
                    m_fileTrace = fopen ( pszEnv, "w" );
                    if ( NULL == m_fileTrace ) {
                        m_fileTrace = stdout;
                    }
                } 
                else {
                    m_fileTrace = stdout;
                }

                setbuf ( m_fileTrace, NULL );
            }

            m_iMaxPacketSize = 8192;
            m_pvPacket = (void *)(new char[m_iMaxPacketSize + sizeof(size_t)]);
            m_piPacketSize = (size_t *)m_pvPacket;
        }

        Protocol::~Protocol()
        {
            if ( m_daemonSocket.isOpened() ) {
                m_daemonSocket.shutdown ();
            }

            if ( NULL != m_pDaemonAddress ) {
                delete m_pDaemonAddress;
                m_pDaemonAddress = NULL;
            }

            if ( NULL != m_fileTrace ) {
                fclose ( m_fileTrace );
                m_fileTrace = NULL;
            }

            if ( NULL != m_fileDebug ) {
                fclose ( m_fileDebug );
                m_fileDebug = NULL;
            }
        }

        ///////////////////////////////////////////////////////////////////////
        //

        void     Protocol::connect ( const char* hostname, unsigned short port, const char* myname )
        {
            trace ( __func__, "enter\n" );

            zsocket::Address*	pAddress = new zsocket::Address ( hostname, port );

            try { 
                if ( !m_daemonSocket.isOpened() ) {
                    m_daemonSocket.open(zsocket::Socket::TCP);
                }
                
                m_daemonSocket.connect ( *pAddress );
            }
            catch(zsocket::Exception& ex) {
                delete pAddress;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to connect  to '%s:%d' -- [%d] %s.", hostname, port, ex.getCode(), ex.what() );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_CONNECT);
            }

            this->m_pDaemonAddress = pAddress;
            if ( NULL != myname ) {
                this->m_strPrivateName = myname;
            }
            else {
                char    _szName[QDX_MAX_PRIVATE_NAME_LEN];
                int     _iLength = snprintf(_szName, sizeof(_szName) - 1, "%s__%u", hostname, port);
                _szName[_iLength] = '\0';
                this->m_strPrivateName = _szName;
            }

            /* STEP. to say hello */
            int		iReturn;
            iReturn = helloDaemon ();
            if ( iReturn < 0 ) {
                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to say HELLO to '%s:%d'.", hostname, port );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_HELLODAEMON);
            }

            m_bReady = true;

            trace ( __func__, "exit\n" );
        }


        void    Protocol::disconnect ( void )
        {
            trace ( __func__, "enter\n" );

            if ( true == m_bReady ) {
                /* STEP. to say byebye */
                int	iReturn = byeDaemon ();
                if ( iReturn < 0 ) {
                    /* error */
                    char    _szExcept[ 256 ];
                    size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to say BYEBYE to '%s'.", this->m_pDaemonAddress->getName().c_str() );
                    _szExcept[ _iEnd ] = '\0';
                    throw Exception(_szExcept, QDXE_BYEDAEMON);
                }

                m_bReady = false;
            }

            m_daemonSocket.shutdown();
            m_daemonSocket.close();

            trace ( __func__, "exit\n" );
        }


        int     Protocol::helloDaemon ( void )
        {
            trace ( __func__, "enter\n" );

            packet_t*	pPkt = (packet_t *)this->getpPacket();
            pPkt->clear ();
            pPkt->setTag ( PACKET_TAG );
            pPkt->setSysPacket ();
            pPkt->setRequestPacket ();
            pPkt->setMessageType ( MESSAGE_CLIENT_HELLO_REQUEST );
            pPkt->setMessageSerial ( this->getMessageSerial() );
            pPkt->setPacketSerial ( 1 );
            pPkt->setPacketTotal ( 1 );

            string	data = "hello\nprivate-name:" + m_strPrivateName + "\naccept-groupship:no\n";
            pPkt->setData ( this->getMaxPacketSize(), data.c_str(), data.size() + 1 );

            this->setPacketSize ( pPkt->getPacketSize() );

            int    timeout = QDX_TIMEOUT;
            size_t	iSizeToSend = pPkt->getPacketSize() + sizeof(size_t);
            trace ( __func__, "HELLO request packet size : %d bytes\n", iSizeToSend );
            bool	bReturn = m_daemonSocket.sendn ( this->getpWholePacket(), iSizeToSend, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to send hello request packet.\n" );
                return	QDXE_HELLO;
            }

            // recv response
            size_t	iSizeToRecv = 0;
            bReturn = m_daemonSocket.recvn ( &iSizeToRecv, sizeof(size_t), timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv sizeof hello response packet.\n" );
                return	QDXE_HELLO;
            }
            char*	pszResponse = new char[ iSizeToRecv ];
            if ( NULL == pszResponse ) {
                trace ( __func__, "fail to allocate response buffer.\n" );
                return	QDXE_HELLO;
            }
            bReturn = m_daemonSocket.recvn ( pszResponse, iSizeToRecv, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv hello response packet.\n" );
                delete [] pszResponse;
                return	QDXE_HELLO;
            }

            // parse response
            pPkt = (packet_t *)pszResponse;
            if ( MESSAGE_CLIENT_HELLO_RESPONSE_NO == pPkt->getMessageType() ) {
                string	strBody ( static_cast<char *>(pPkt->getpData()) );
                vector< string >	status;
                util::split ( strBody, " ", status, 2 );
                int		iStatusCode = atoi ( status[0].c_str() );

                trace ( __func__, "hello deny by daemon : %s\n", strBody.c_str() );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "HELLO denied by daemon '%s:%d' : %s.", this->m_pDaemonAddress->getIPString().c_str(), this->m_pDaemonAddress->getPort(), strBody.c_str() );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_HELLODAEMON);
                //return	iStatusCode;
            }

            string	strBody ( static_cast<char *>(pPkt->getpData()) );
            trace ( __func__, "HELLO response OK.\n%s", strBody.c_str() );
            int	iReturn = parseHelloResponseOK ( strBody );
            if ( iReturn < 0 ) {
                delete [] pszResponse;
                return	QDXE_HELLO;
            }

            delete [] pszResponse;

            trace ( __func__, "exit\n" );

            return	0;
        }

        int     Protocol::parseHelloResponseOK ( string& strBody )
        {
            map<string, string>	bodies;
            map<string, string>::iterator	result;

            int	iReturn = parseProtocolBody ( strBody, bodies );
            if ( iReturn < 0 ) {
                trace ( __func__, "fail to parse protocol body.\n" );
                return	-1;
            }

            result = bodies.find ( "daemon-name" );
            if (bodies.end() == result) {
                return -2;
            }
            m_strDaemon = result->second;

            result = bodies.find ( "private-group-name" );
            if (bodies.end() == result) {
                return -2;
            }
            m_strPrivateGroupName = result->second;

            result = bodies.find ( "daemon-id" );
            if (bodies.end() == result) {
                return -2;
            }
            string strTmp = result->second;
            m_iDaemonID = atoi(strTmp.c_str());

            result = bodies.find ( "client-id" );
            if (bodies.end() == result) {
                return -2;
            }
            strTmp = result->second;
            m_iClientID = atoi(strTmp.c_str());

            result = bodies.find ( "packet-size" );
            if (bodies.end() == result) {
                return -2;
            }
            strTmp = result->second;
            trace ( __func__, "temporary packet size : %d\t default packet size : %s\n", m_iMaxPacketSize, strTmp.c_str() );
            m_iMaxPacketSize = atoi(strTmp.c_str());
            delete [] (char *)m_pvPacket;
            m_pvPacket = (void *)(new char[m_iMaxPacketSize + sizeof(size_t)]);
            m_piPacketSize = (size_t *)m_pvPacket;

            return	0;
        }

        int     Protocol::parseProtocolBody ( string& strBody, map<string, string>& fields )
        {
            size_t      iReturn;

            std::vector<std::string> lines;
            iReturn = util::split ( strBody, "\n", lines );
            if ( iReturn < 2 ) {
                return  -1;
            }

            string  name = lines[0];
            fields[ string("name") ] = name;
            for ( size_t i = 1; i < lines.size(); i++ ) {
                vector<string>  flds;
                iReturn = util::split ( lines[i], ":", flds, 2 );
                util::trim ( flds[0] );
                util::trim ( flds[1] );
                fields[ flds[0] ] = flds[1];
            }

            return  fields.size();
        }

        int     Protocol::byeDaemon ( void )
        {
            trace ( __func__, "enter\n" );

            packet_t*	pPkt = (packet_t *)this->getpPacket();
            pPkt->clear ();
            pPkt->setTag ( PACKET_TAG );
            pPkt->setSysPacket ();
            pPkt->setRequestPacket ();
            pPkt->setDaemonID ( m_iDaemonID );
            pPkt->setClientID ( m_iClientID );
            pPkt->setSender ( m_strPrivateGroupName.c_str() );
            pPkt->setMessageType ( MESSAGE_CLIENT_BYE_REQUEST );
            pPkt->setMessageSerial ( this->getMessageSerial() );
            pPkt->setPacketSerial ( 1 );
            pPkt->setPacketTotal ( 1 );

            string	data = "bye\nprivate-group-name:" + m_strPrivateGroupName + "\n";
            pPkt->setData ( this->getMaxPacketSize(), data.c_str(), data.size() + 1 );

            this->setPacketSize ( pPkt->getPacketSize() );

            int	    timeout = QDX_TIMEOUT;
            size_t	iSizeToSend = pPkt->getPacketSize() + sizeof(size_t);
            trace ( __func__, "BYE request packet size : %d bytes\n", iSizeToSend );
            bool	bReturn = m_daemonSocket.sendn ( this->getpWholePacket(), iSizeToSend, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to send BYE request packet.\n" );
                return	QDXE_BYE;
            }

            // recv response
            size_t	iSizeToRecv = 0;
            bReturn = m_daemonSocket.recvn ( &iSizeToRecv, sizeof(size_t), timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv sizeof BYE response packet.\n" );
                return	QDXE_BYE;
            }
            char*	pszResponse = new char[ iSizeToRecv ];
            if ( NULL == pszResponse ) {
                trace ( __func__, "fail to allocate response buffer.\n" );
                return	QDXE_BYE;
            }
            bReturn = m_daemonSocket.recvn ( pszResponse, iSizeToRecv, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv BYE response packet.\n" );
                delete [] pszResponse;
                return	QDXE_BYE;
            }

            // parse response
            pPkt = (packet_t *)pszResponse;
            if ( MESSAGE_CLIENT_BYE_RESPONSE_NO == pPkt->getMessageType() ) {
                string	strBody ( static_cast<char *>(pPkt->getpData()) );
                vector< string >	status;
                util::split ( strBody, " ", status, 2 );
                int		iStatusCode = atoi ( status[0].c_str() );

                trace ( __func__, "BYE deny by daemon : %s\n", strBody.c_str() );
                delete [] pszResponse;
                return	iStatusCode;
            }
            trace ( __func__, "BYE response OK.\n%s", static_cast<char *>(pPkt->getpData()) );

            delete [] pszResponse;

            trace ( __func__, "exit\n" );

            return	0;
        }

        ///////////////////////////////////////////////////////////////////////
        //

        void      Protocol::join ( const char* pszGroup )
        {

            trace ( __func__, "enter\n" );

            packet_t*	pPkt = (packet_t *)this->getpPacket();
            pPkt->clear ();
            pPkt->setTag ( PACKET_TAG );
            pPkt->setSysPacket ();
            pPkt->setRequestPacket ();
            pPkt->setDaemonID ( m_iDaemonID );
            pPkt->setClientID ( m_iClientID );
            pPkt->setSender ( m_strPrivateGroupName.c_str() );
            pPkt->setMessageType ( MESSAGE_JOIN_GROUP_REQUEST );
            pPkt->setMessageSerial ( this->getMessageSerial() );
            pPkt->setPacketSerial ( 1 );
            pPkt->setPacketTotal ( 1 );

            string	data = "join\ngroup:" + string(pszGroup) + "\n";
            pPkt->setData ( this->getMaxPacketSize(), data.c_str(), data.size() + 1 );

            this->setPacketSize ( pPkt->getPacketSize() );

            int	timeout = QDX_TIMEOUT;
            size_t	iSizeToSend = pPkt->getPacketSize() + sizeof(size_t);
            trace ( __func__, "JOIN request packet size : %d bytes\n", iSizeToSend );
            bool	bReturn = m_daemonSocket.sendn ( this->getpWholePacket(), iSizeToSend, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to send JOIN request packet.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to send JOIN request (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_JOIN);
            }

            // recv response
            size_t	iSizeToRecv = 0;
            bReturn = m_daemonSocket.recvn ( &iSizeToRecv, sizeof(size_t), timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv sizeof JOIN response packet.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to recv JOIN response size (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_JOIN);
            }
            char*	pszResponse = new char[ iSizeToRecv ];
            if ( NULL == pszResponse ) {
                trace ( __func__, "fail to allocate response buffer.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to allocate buffer for JOIN response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_JOIN);
            }
            bReturn = m_daemonSocket.recvn ( pszResponse, iSizeToRecv, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv JOIN response packet.\n" );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to recv JOIN response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_JOIN);
            }

            // parse response
            pPkt = (packet_t *)pszResponse;
            if ( MESSAGE_JOIN_GROUP_RESPONSE_NO == pPkt->getMessageType() ) {
                string	strBody ( static_cast<char *>(pPkt->getpData()) );
                vector< string >	status;
                util::split ( strBody, " ", status, 2 );
                int		iStatusCode = atoi ( status[0].c_str() );

                trace ( __func__, "JOIN deny by daemon : %s\n", strBody.c_str() );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to JOIN  (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, iStatusCode);
            }
            trace ( __func__, "JOIN response OK.\n%s", static_cast<char *>(pPkt->getpData()) );

            delete [] pszResponse;

            trace ( __func__, "exit\n" );
        }

        void         Protocol::leave ( const char* pszGroup )
        {
            trace ( __func__, "enter\n" );

            packet_t*	pPkt = (packet_t *)this->getpPacket();
            pPkt->clear ();
            pPkt->setTag ( PACKET_TAG );
            pPkt->setSysPacket ();
            pPkt->setRequestPacket ();
            pPkt->setDaemonID ( m_iDaemonID );
            pPkt->setClientID ( m_iClientID );
            pPkt->setSender ( m_strPrivateGroupName.c_str() );
            pPkt->setMessageType ( MESSAGE_LEAVE_GROUP_REQUEST );
            pPkt->setMessageSerial ( this->getMessageSerial() );
            pPkt->setPacketSerial ( 1 );
            pPkt->setPacketTotal ( 1 );

            string	data = "leave\ngroup:" + string(pszGroup) + "\n";
            pPkt->setData ( this->getMaxPacketSize(), data.c_str(), data.size() + 1 );

            this->setPacketSize ( pPkt->getPacketSize() );

            int	    timeout = QDX_TIMEOUT;
            size_t	iSizeToSend = pPkt->getPacketSize() + sizeof(size_t);
            trace ( __func__, "LEAVE request packet size : %d bytes\n", iSizeToSend );
            bool	bReturn = m_daemonSocket.sendn ( this->getpWholePacket(), iSizeToSend, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to send LEAVE request packet.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to send LEAVE request (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_LEAVE);
            }

            // recv response
            size_t	iSizeToRecv = 0;
            bReturn = m_daemonSocket.recvn ( &iSizeToRecv, sizeof(size_t), timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv sizeof LEAVE response packet.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to recv LEAVE response size (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_LEAVE);
            }
            char*	pszResponse = new char[ iSizeToRecv ];
            if ( NULL == pszResponse ) {
                trace ( __func__, "fail to allocate response buffer.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to allocate buffer for LEAVE response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_LEAVE);
            }
            bReturn = m_daemonSocket.recvn ( pszResponse, iSizeToRecv, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv LEAVE response packet.\n" );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to recv LEAVE response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_LEAVE);
            }

            // parse response
            pPkt = (packet_t *)pszResponse;
            if ( MESSAGE_LEAVE_GROUP_RESPONSE_NO == pPkt->getMessageType() ) {
                string	strBody ( static_cast<char *>(pPkt->getpData()) );
                vector< string >	status;
                util::split ( strBody, " ", status, 2 );
                int		iStatusCode = atoi ( status[0].c_str() );

                trace ( __func__, "LEAVE deny by daemon : %s\n", strBody.c_str() );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to LEAVE  (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, iStatusCode);
            }
            trace ( __func__, "LEAVE response OK.\n%s", static_cast<char *>(pPkt->getpData()) );

            delete [] pszResponse;

            trace ( __func__, "exit\n" );
        }

        int     Protocol::query ( const char* pszGroup, vector<string>& joiners )
        {
            trace ( __func__, "enter\n" );

            packet_t*	pPkt = (packet_t *)this->getpPacket();
            pPkt->clear ();
            pPkt->setTag ( PACKET_TAG );
            pPkt->setSysPacket ();
            pPkt->setRequestPacket ();
            pPkt->setDaemonID ( m_iDaemonID );
            pPkt->setClientID ( m_iClientID );
            pPkt->setSender ( m_strPrivateGroupName.c_str() );
            pPkt->setMessageType ( MESSAGE_QUERY_GROUP_REQUEST );
            pPkt->setMessageSerial ( this->getMessageSerial() );
            pPkt->setPacketSerial ( 1 );
            pPkt->setPacketTotal ( 1 );

            string	strGroup (pszGroup);
            string	data = "query\ngroup:" + strGroup + "\n";
            pPkt->setData ( this->getMaxPacketSize(), data.c_str(), data.size() + 1 );

            this->setPacketSize ( pPkt->getPacketSize() );

            int	    timeout = QDX_TIMEOUT;
            size_t	iSizeToSend = pPkt->getPacketSize() + sizeof(size_t);
            trace ( __func__, "QUERY request packet size : %d bytes\n", iSizeToSend );
            bool	bReturn = m_daemonSocket.sendn ( this->getpWholePacket(), iSizeToSend, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to send QUERY request packet.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to send QUERY request (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_QUERY);
            }

            // recv response
            size_t	iSizeToRecv = 0;
            bReturn = m_daemonSocket.recvn ( &iSizeToRecv, sizeof(size_t), timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv sizeof QUERY response packet.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to recv QUERY response size (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_QUERY);
            }
            char*	pszResponse = new char[ iSizeToRecv ];
            if ( NULL == pszResponse ) {
                trace ( __func__, "fail to allocate response buffer.\n" );

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to allocate buffer for QUERY response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_QUERY);
            }
            bReturn = m_daemonSocket.recvn ( pszResponse, iSizeToRecv, timeout );
            if ( false == bReturn ) {
                trace ( __func__, "fail to recv QUERY response packet.\n" );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to recv QUERY response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_QUERY);
            }

            // parse response
            pPkt = (packet_t *)pszResponse;
            if ( MESSAGE_QUERY_GROUP_RESPONSE_NO == pPkt->getMessageType() ) {
                string	strBody ( static_cast<char *>(pPkt->getpData()) );
                vector< string >	status;
                util::split ( strBody, " ", status, 2 );
                int		iStatusCode = atoi ( status[0].c_str() );

                trace ( __func__, "QUERY deny by daemon : %s\n", strBody.c_str() );
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to QUERY  (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, iStatusCode);
            }
            string	strBody ( static_cast<char *>(pPkt->getpData()) );
            trace ( __func__, "QUERY response OK.\n%s\n", strBody.c_str() );
            int	iReturn = parseQueryResponseOK ( strGroup, strBody, joiners );
            if ( iReturn < 0 ) {
                delete [] pszResponse;

                char    _szExcept[ 256 ];
                size_t  _iEnd = snprintf( _szExcept, sizeof(_szExcept) - 1, "Fail to parse QUERY response (group '%s').", pszGroup );
                _szExcept[ _iEnd ] = '\0';
                throw Exception(_szExcept, QDXE_QUERY);
            }
            int	iCountJoiners = iReturn;

            delete [] pszResponse;

            trace ( __func__, "exit\n" );

            return	iCountJoiners;
        }

        int     Protocol::parseQueryResponseOK ( string& strGroupToQuery, string& strBody, vector<string>& joiners )
        {
            map<string, string>	bodies;
            map<string, string>::iterator	result;

            int	iReturn = parseProtocolBody ( strBody, bodies );
            if ( iReturn < 0 ) {
                trace ( __func__, "fail to parse protocol body.\n" );
                return	-1;
            }

            result = bodies.find ( "count" );
            if (bodies.end() == result) {
                return -2;
            }
            string	strTmp = result->second;
            int		iCount = atoi ( strTmp.c_str() );
            if (iCount < 1)
            {
                return	iCount;
            }

            result = bodies.find ( strGroupToQuery );
            if (bodies.end() == result)
            {
                return	-3;
            }
            strTmp = result->second;
            vector<string>	items;
            util::split ( strTmp, " ", items );
            for (size_t i = 0; i < items.size(); i++)
            {
                joiners.push_back ( items[i] );
            }

            return	iCount;
        }


        ///////////////////////////////////////////////////////////////////////
        //

        void     Protocol::send ( Message& message, int timeout )
        {
            trace ( __func__, "enter\n" );

            trace ( __func__, "exit\n" );
        }

        void     Protocol::recv ( Message& message, int timeout )
        {
            trace ( __func__, "enter\n" );

            trace ( __func__, "exit\n" );
        }


        ///////////////////////////////////////////////////////////////////////
        //

        std::string
            Protocol::strerror ( int errno ) const
            {

                return	"QDX::Client::ERROR";
            }

        ///////////////////////////////////////////////////////////////////////
        //

        void*
            Protocol::getpPacket ( void ) const
            {
                return	(void *)((char *)m_pvPacket + sizeof(size_t) );
            }

        void*
            Protocol::getpWholePacket ( void ) const
            {
                return	m_pvPacket;
            }

        void
            Protocol::setPacketSize ( size_t iPacketSize )
            {
                *m_piPacketSize = iPacketSize;
            }

        size_t
            Protocol::getPacketSize ( void ) const
            {
                return	*m_piPacketSize;
            }

        void*
            Protocol::getpPacketData ( void ) const
            {
                packet_t*	pPkt = (packet_t *)getpPacket();

                return	pPkt->getpData();
            }

        size_t
            Protocol::getMaxPacketSize ( void ) const
            {
                return	m_iMaxPacketSize;
            }

        size_t
            Protocol::getMessageSerial ( void )
            {
                if ( m_iMessageSerial >= 65000 ) {
                    m_iMessageSerial = 0;
                }

                ++m_iMessageSerial;
                return	m_iMessageSerial;
            }

        ///////////////////////////////////////////////////////////////////////
        //

        static    int     getTimeString ( char* pszBuf, size_t iBufSize );

        void     Protocol::debug ( const char* pszFormat, ... )
        {
            if ( NULL == m_fileDebug ) {
                return;
            }

            va_list	args;

            va_start ( args, pszFormat );
            vfprintf ( m_fileDebug, pszFormat, args );
            va_end ( args );
        }

        void     Protocol::trace ( const char* pszFunc, const char* pszFormat, ... )
        {
            if ( NULL == m_fileTrace ) {
                return;
            }

            va_list	args;

            if ( NULL != pszFunc ) {
                char    szTimestamp[64];
                getTimeString(szTimestamp, sizeof(szTimestamp));
                fprintf ( m_fileTrace, "%s %s() ", szTimestamp, pszFunc );
            }

            va_start ( args, pszFormat );
            vfprintf ( m_fileTrace, pszFormat, args );
            va_end ( args );
        }

        static    int     getTimeString ( char* pszBuf, size_t iBufSize )
        {
            struct tm*  tmp;
            time_t  t;
            int     iReturn;
            
            pszBuf[0] = '\0';
            
            t = time (NULL);
            tmp = localtime( &t );
            if ( NULL == tmp ) {
                return  -1;
            }
            
            pszBuf[0] = '\0';
            iReturn = strftime(pszBuf, iBufSize, "%Y-%m-%d %H:%M:%S", tmp);
            if (iReturn > 0) {
                pszBuf[ iReturn ] = '\0';
            }
            
            return  0;
        }   

    }	/* namespace Client */
}	/* namespace QDX */


